#pragma once
#include <vector>
#include <algorithm>
#include <random>
#include <iostream>
#include <thread>
#include <functional>

template <typename T>
class CInfiniteRandomIterator
{
    using VecType = std::vector<T>;
public:

    CInfiniteRandomIterator(const VecType& v) : m_v(v), m_Engine(42) {
        shuffleV();
    }

    void shuffleV() {
        std::shuffle(std::begin(m_v), std::end(m_v), m_Engine);
        m_I = 0;
    }

    T next() {
        T ret = m_v[m_I++];
        if (m_I >= m_v.size()) shuffleV();
        return ret;
    }
private:
    VecType m_v;
    size_t m_I;
    std::default_random_engine m_Engine;
};

template <typename IndexType, typename FuncType>
void parallel_for(IndexType vBegin, IndexType vEnd, FuncType vFunc) {
    size_t Range = vEnd - vBegin;
    if (Range <= 0) return;
    size_t NumThreads = (std::min)(static_cast<size_t>(std::thread::hardware_concurrency()), Range);
    size_t ChunkSize = (Range + NumThreads - 1) / NumThreads;
    std::vector<std::thread> Threads;

    for (unsigned int i = 0; i < NumThreads; i++) {
        IndexType ChunkBegin = vBegin + i * ChunkSize;
        IndexType ChunkEnd = (std::min)(ChunkBegin + ChunkSize, vEnd);

        Threads.emplace_back([ChunkBegin, ChunkEnd, &vFunc]() {
            for (IndexType Item = ChunkBegin; Item < ChunkEnd; Item++) {
                vFunc(*Item);
            }
            });
    }

    for (std::thread& Thread : Threads) {
        Thread.join();
    }
}

std::string getRandomStr(int vLength);
